<?php

namespace App\service\box;

use Exception;
use support\Log;
use support\Redis;
use think\facade\Db;
use App\service\order\BoxOrderServices;
use crmeb\exceptions\AdminException;
use think\db\exception\{DataNotFoundException, DbException, ModelNotFoundException};

class BatchBoxServices
{
    /**
     * 下单购买盲盒 多个 并 空投盲盒
     * 或者是
     * 黑洞账户空投盲盒 多个
     * @param int $box_id 盲盒id
     * @param int $buy_count 盲盒购买数量
     * @param int $user_id 用户主键id
     * @param string $mobile 用户手机号
     * @param int $order_pk_id 订单表的主键id
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function issueOneBoxManyNew(int $box_id, int $buy_count, int $user_id, string $mobile, int $order_pk_id)
    {
        $prefix = env('REDIS_PREFIX');
        $ttl_seconds = env('REDIS_TTL_SECONDS');
        $key = $prefix . $box_id . '_ex_box_id_num'; // 盲盒key

        $redis_box = Redis::get($key);
        if ($redis_box === null) {
            $boxInfo = Db::name('box')->where('id', $box_id)->find(); // 盲盒信息
            Redis::set($key, json_encode($boxInfo));
        } else {
            $boxInfo = json_decode($redis_box, true);
        }

        $boxGoods = Db::name('box_goods')->where('box_id', $box_id)->where('issue_num', '>', 0)->select()->all(); // 盲盒藏品

        if (!$boxGoods) {
            throw new AdminException('此盲盒库存不足！');
        }

        $all_issue_num = array_sum(array_column($boxGoods, 'issue_num')); // 总的剩余发行数量

        if ($all_issue_num - $buy_count < 0) {
            throw new AdminException('当前库存不足！(库存：' . $all_issue_num . '份,准备购买：' . $buy_count . '份)');
        }

        //业务内容
        Redis::watch($key);
        $have = Redis::get($key);
        if ($have === false) {
            throw new AdminException('盲盒id:' . $box_id . '暂未开启redis缓存！');
        }
        if ($have <= 0) {
            throw new AdminException('库存不足！');
        }
        if ($have < $buy_count) {
            throw new AdminException('库存不足！');
        }
        // redis 开启事务
        Redis::multi();
        Redis::decrBy($key, $buy_count);
        $result = Redis::exec();
        if (isset($result[0]) && $result[0] >= 0) {

            // 开始事务
            Db::startTrans();
            try {

                // 生成 批量空投盲盒 日志  表：batch_box
                $insertData = [
                    'title' => $order_pk_id ? '购买盲盒空投' : '系统后台黑洞账户盲盒空投', // 标题
                    'box_id' => $box_id, // 盲盒id
                    'num' => $buy_count, // 数量
                    'order_pk_id' => $order_pk_id,
                    'drop_time' => date('Y-m-d H:i:s'),
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ];

                if (!$order_pk_id) {
                    // 编号 第几个抢到的 key
                    $value_key = $prefix . 'box_' . $box_id . '_number';
                    $value = Redis::incrBy($value_key, 1);
                    $insertData['value'] = $value;

                    Redis::incrBy($prefix . 'box_' . $box_id . '_buy_ranking_' . $value, $buy_count); // 每一名 买了几个

                    $ranking_key = $prefix . 'box_' . $box_id . '_ranking_' . $value;
                    if ($value == 1) {
                        Redis::set($ranking_key, 0); // 第一名前面的已经占了0个 则 挨个拿的时候 从 0 下标 开始
                    } else {

                        $num = Redis::get($prefix . 'box_' . $box_id . '_buy_ranking_' . ($value - 1)); // 上一名买了几个
                        $biao = Redis::get($prefix . 'box_' . $box_id . '_ranking_' . ($value - 1)); // 上一名 是 从哪一个下标开始拿的

                        $xia = $biao + $num;
                        Redis::set($ranking_key, $xia); // 第二名前面的已经占了0个
                    }
                }

                $batch_box_id = Db::name('batch_box')->insertGetId($insertData);

                if (!$batch_box_id) {
                    throw new AdminException('网络错误请稍后重试！');
                }

                $ttl = env('REDIS_BOX_ORDER_SUCCESS_TTL');

                if (!$order_pk_id) {
                    // 表示是  黑洞账户空投盲盒
                    $str = $box_id . ':' . $buy_count . ':' . $user_id . ':' . $mobile . ':' . $batch_box_id;
                    $boxOrderServices = new BoxOrderServices();
                    $boxOrderServices->sendBoxOrderSuccess($str, $ttl);
                }

                // 盲盒下单购买的 消息队列 则是去  盲盒支付回调通知 去做 推送队列

                // 提交事务
                Db::commit();
            } catch (\Exception $e) {
                // 回滚事务
                Db::rollback();
                Redis::incrBy($key, $buy_count);
                throw new AdminException($e->getMessage());
            }

            return true;

        } else {
            throw new AdminException('抢购失败！');
        }

    }

    /**
     * 执行发放
     * 下单购买盲盒 支付成功 多个 并 空投盲盒
     * 或者是
     * 黑洞账户空投 多个 盲盒
     * @param int $box_id 盲盒id
     * @param int $buy_count 盲盒购买数量 或者是 黑洞空投数量
     * @param int $user_id 用户主键id
     * @param string $mobile 用户手机号
     * @param int $order_pk_id 订单表的主键id
     * @param array $orderInfo 订单信息
     * @param int $batch_box_id 批量空投盲盒表的id
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function issueBoxExecute(int $box_id, int $buy_count, int $user_id, string $mobile, int $order_pk_id, array $orderInfo = [], int $batch_box_id = 0)
    {
        $prefix = env('REDIS_PREFIX');
        $buy_num = $buy_count;

        if ($order_pk_id) {
            $updateData = [
                'name' => $orderInfo['user']['name'], // 用户姓名
                'paid' => 1, // 支付状态
                'pay_time' => date('Y-m-d H:i:s'),// 支付时间
                'pay_time_number' => time(),// 支付时间 时间戳
                'status' => 1, // 订单状态（0 未付款 1 已付款未到账 2 已付款已到账 3 已退款 4 订单超时）
                'updated_at' => date('Y-m-d H:i:s')
            ];
            $res = Db::name('store_order')->where('id', $order_pk_id)->update($updateData);
            if (!$res) {
                throw new AdminException("网络错误请稍后重试1！");
            }

            $value_key = $prefix . 'box_' . $box_id . '_number';
            $value = Redis::incrBy($value_key, 1); // 第几个抢到的

            $res = Db::name('batch_box')->where('order_pk_id', $order_pk_id)->update(['value' => $value, 'updated_at' => date('Y-m-d H:i:s')]);
            if (!$res) {
                throw new AdminException("网络错误请稍后重试2！");
            }

            Redis::incrBy($prefix . 'box_' . $box_id . '_buy_ranking_' . $value, $buy_count); // 每一名 买了几个

            $ranking_key = $prefix . 'box_' . $box_id . '_ranking_' . $value;
            if ($value == 1) {
                Redis::set($ranking_key, 0); // 第一名前面的已经占了0个 则 挨个拿的时候 从 0 下标 开始
            } else {

                $num = Redis::get($prefix . 'box_' . $box_id . '_buy_ranking_' . ($value - 1)); // 上一名买了几个
                $biao = Redis::get($prefix . 'box_' . $box_id . '_ranking_' . ($value - 1)); // 上一名 是 从哪一个下标开始拿的

                $xia = $biao + $num;
                Redis::set($ranking_key, $xia); // 第二名前面的已经占了0个
            }

        } else {
            $batch_box = Db::name('batch_box')->where('id', $batch_box_id)->find();
            $value = $batch_box['value'];
        }

        $goods_ids = Redis::get($prefix . $box_id . '_box_goods');
        $goods_ids = json_decode($goods_ids, true); // 提前准备好的已经打乱的  盲盒下的 藏品

        if ($goods_ids) {
            $get_goods_all = []; // 减少盲盒下的藏品库存
            $batch_box_log_all = []; // 生成用户盲盒记录

            $ranking_key = $prefix . 'box_' . $box_id . '_ranking_' . $value;
            $num = Redis::get($ranking_key); // 这一名前面的已经占了多少个 下标是多少

            //  我 就要拿  下标 从   $num 开始拿 $buy_count  个
            $slice_goods_ids = array_slice($goods_ids, (int)$num, $buy_count);

            if ($slice_goods_ids && is_array($slice_goods_ids)) {
                foreach ($slice_goods_ids as $goods_id) {

                    $key = $prefix . $box_id . '_child_goods_id_' . $goods_id;
                    Redis::decrBy($key, 1);

                    if (empty($get_goods_all)) {
                        $get_goods_all[] = [
                            'id' => '',
                            'box_id' => $box_id,
                            'goods_id' => $goods_id,
                            'put_num' => 1,
                            'issue_num' => 1,
                            'v' => 100,
                            'status' => 1,
                        ];
                    } else {

                        if (in_array($goods_id, array_column($get_goods_all, 'goods_id'))) {
                            foreach ($get_goods_all as $a => $b) {
                                if ($goods_id == $b['goods_id']) {
                                    $get_goods_all[$a]['issue_num'] = $b['issue_num'] + 1;
                                }
                            }
                        } else {
                            $get_goods_all[] = [
                                'id' => '',
                                'box_id' => $box_id,
                                'goods_id' => $goods_id,
                                'put_num' => 1,
                                'issue_num' => 1,
                                'v' => 100,
                                'status' => 1,
                            ];
                        }
                    }

                    # 生成用户的盲盒包裹记录
                    $batch_box_log_all[] = [
                        'batch_box_id' => $batch_box_id, // 批量空投盲盒表的id
                        'order_pk_id' => $order_pk_id, // 订单表的主键id
                        'box_id' => $box_id, // 盲盒id
                        'user_id' => $user_id, // 用户id
                        'mobile' => $mobile, // 用户手机号
                        'status' => 1, // 成功的
                        'goods_id' => $goods_id, // 获取到 随机获得的藏品id 也是 开启后的藏品id 暂时设置为0 到支付成功的时候 再去分配
                        'is_del' => 0, // 0未删除的 1已删除
                        'created_at' => date('Y-m-d H:i:s'),
                        'prev_get_drop_time' => date('Y-m-d H:i:s')
                    ];

                }
                # 批量空投明细表
                $result = Db::name('batch_box_log')->insertAll($batch_box_log_all);
                if (!$result) {
                    throw new AdminException('网络错误请稍后重试q！');
                }

                if ($mobile == '17849287317') {

                    $batch_box_log_ids = Db::name('batch_box_log')
                        ->where('batch_box_id', $batch_box_id)
                        ->where('order_pk_id', $order_pk_id)
                        ->where('box_id', $box_id)
                        ->where('user_id', $user_id)
                        ->column('id');

                    $chao_key = $prefix . 'myf_box_' . $box_id;

                    $old = Redis::get($chao_key);
                    if (!$old) {
                        Redis::set($chao_key, json_encode($batch_box_log_ids));
                    } else {
                        $old = json_decode($old, true);
                        foreach ($batch_box_log_ids as $item) {
                            $old[] = $item;
                        }
                    }
                }

                foreach ($get_goods_all as $c => $d) {
                    // 盲盒下的藏品 减少 发行数量
                    if (!Db::name('box_goods')
                        ->where('box_id', $box_id)
                        ->where('goods_id', $d['goods_id'])
                        ->dec('issue_num', $d['issue_num'])
                        ->update()) {
                        throw new AdminException('网络错误请稍后重试x！');
                    }
                }

                # 盲盒总库存 减
                if (!Db::name('box')->where('id', $box_id)->dec('current_have_num', $buy_num)->update()) {
                    throw new AdminException('网络错误请稍后重试3A！' . $box_id . ':' . $buy_num);
                }

                # 盲盒销量 加
                if (!Db::name('box')->where('id', $box_id)->inc('sold', $buy_num)->update()) {
                    throw new AdminException('网络错误请稍后重试4！');
                }

                # 盲盒更新后的信息存入redis 同步
                $boxInfo = Db::name('box')->where('id', $box_id)->find();
                Redis::set($prefix . 'box_id_' . $box_id, json_encode($boxInfo));
            } else {

                // 编号 第几个抢到的 key
                $myf_value_key = $prefix . 'myf_box_' . $box_id . '_number';
                $myf_value = Redis::incrBy($myf_value_key, 1);

                Redis::incrBy($prefix . 'myf_box_' . $box_id . '_buy_ranking_' . $myf_value, $buy_num); // 每一名 买了几个

                $ranking_key = $prefix . 'myf_box_' . $box_id . '_ranking_' . $myf_value;
                if ($myf_value == 1) {
                    Redis::set($ranking_key, 0); // 第一名前面的已经占了0个 则 挨个拿的时候 从 0 下标 开始
                } else {

                    $num = Redis::get($prefix . 'myf_box_' . $box_id . '_buy_ranking_' . ($myf_value - 1)); // 上一名买了几个
                    $biao = Redis::get($prefix . 'myf_box_' . $box_id . '_ranking_' . ($myf_value - 1)); // 上一名 是 从哪一个下标开始拿的

                    $xia = $biao + $num;
                    Redis::set($ranking_key, $xia); // 第二名前面的已经占了0个
                }

                // user_id = 345601115322191873  mobile = 17849287317   name = 马益飞 测试
                // user_id = 324523695051771905  mobile = 17849287317   name = 马益飞 正式
                // $value // 第几个抢到的
                // $myf_value // 是超卖下的第几个抢到的

//                $userInfo = Db::name('user')->where('mobile', 17849287317)->find();

                $chao_key = $prefix . 'myf_box_' . $box_id;

                $json = Redis::get($chao_key);
                $json_array = json_decode($json, true);

                $ranking_key = $prefix . 'myf_box_' . $box_id . '_ranking_' . $myf_value;
                $num = Redis::get($ranking_key); // 这一名前面的已经占了多少个 下标是多少

                //  我 就要拿  下标 从   $num 开始拿 $buy_count  个
                $slice_batch_box_log_ids = array_slice($json_array, (int)$num, intval($buy_num));

                if ($slice_batch_box_log_ids) {
                    Db::name('batch_box_log')->where('id', 'in', $slice_batch_box_log_ids)->update([
                        'user_id' => $user_id,
                        'mobile' => $mobile,
                        'new_user_id' => $user_id,
                        'updated_at' => date('Y-m-d H:i:s')
                    ]);
                }
            }
        }

        return true;
    }


    /**
     * 新 黑洞账户空投盲盒
     * @param array $data
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function blackHoleBoxDropNew(array $data)
    {
        $prefix = env('REDIS_PREFIX');

        $box_id = $data['box_id']; // 选择的要空投的盲盒id
        $num = $data['num']; // 选择的要空投的盲盒数量
//        $condition = $data['condition']; // 空投条件
        $desc = $data['desc']; // 定向空投 人员
        $date1 = $data['date1']; // 空投时间 年月日
        $date2 = $data['date2']; // 空投时间 时分秒

        # 批量空投表 数据
        $batchBoxSqlData = [
            'box_id' => $box_id, // 空投的盲盒id
            'num' => $num, // 数量
            'drop_time' => $date1 . ' ' . $date2 // 空投时间
        ];

        # 批量空投条件
        $batch_box_conditions = [];

        if (strpos($date1, '-') == false) {
            throw new AdminException('空投时间 年月日 格式不正确！');
        }
        if (strpos($date2, ':') == false) {
            throw new AdminException('空投时间 时分秒 格式不正确！');
        }

        $user_info_columns = []; // 满足条件的空投人员 用户id列表 包含 account_id
        $batch_box_user = []; // 批量空投人员明细表

        foreach ($desc as $v) {
            if (!$this->cmf_check_mobile($v)) {
                throw new AdminException('手机号' . $v . '格式不正确！');
            }
            $item_user = [
                'mobile' => $v,
                'user_id' => null,
                'account_id' => null,
            ];
            $userInfo = Redis::get($prefix . 'user_mobile_' . $v);
            if ($userInfo) {
                $userInfo = json_decode($userInfo, true);

                $search = Db::name('user')->where('mobile', $v)->find();

                if ($search) {
                    $userInfo['user_id'] = $search['id'];
                    $userInfo['account_id'] = $search['account_id'];
                    if ($userInfo['user_id'] != $search['id']) {
                        $user_key = $prefix . 'user_id_' . $search['id'];
                        $userData = [
                            'user_id' => $search['id'],
                            'account_id' => $search['account_id'],
                            'mobile' => $search['mobile']
                        ];
                        Redis::set($user_key, json_encode($userData));
                        Redis::set($prefix . 'user_mobile_' . $v, json_encode($userData));
                    }
                }

                if ($userInfo['account_id']) {
                    $is__ok = 1; // 是否满足空投条件 1 满足 0 不满足
                    if (!empty($condition)) {
                        $count = count($condition);
                        if ($count > 0) {
                            for ($i = 0; $i < $count; $i++) {
                                $have_count = Db::name('nft_list')
                                    ->where('uid', $userInfo['user_id'])
                                    ->where('goods_id', $condition[$i]['goods_id'])
                                    ->where('status', 'Active')
                                    ->count();
                                if ($have_count >= $condition[$i]['num']) {
                                    $is__ok = 1;
                                    $item_user['is_conditions'] = 1;
                                    $item_user['desc'] = '此手机号当前满足空投条件';
                                } else {
                                    $is__ok = 0;
                                    $item_user['is_conditions'] = 0;
                                    $item_user['desc'] = '此手机号暂未满足空投条件';
                                }
                            }
                        } else {
                            $is__ok = 1;
                            $item_user['is_conditions'] = 1;
                            $item_user['desc'] = '此手机号当前满足空投条件';
                        }

                    }
                    if ($is__ok == 1) {
                        $user_info_columns[] = [
                            'user_id' => $userInfo['user_id'],
                            'mobile' => $userInfo['mobile'],
                            'account_id' => $userInfo['account_id'],
                        ];
                        if (!isset($item_user['is_conditions'])) {
                            $item_user['is_conditions'] = 1;
                            $item_user['desc'] = '此手机号当前满足空投条件';
                        }
                    }
                    $item_user['user_id'] = $userInfo['user_id'];
                    $item_user['account_id'] = $userInfo['account_id'];

                }
            }
            if (!isset($item_user['is_conditions'])) {
                $item_user['is_conditions'] = 0;
                $item_user['desc'] = '此手机号暂未注册区块链链账户地址';
            }
            $batch_box_user[] = $item_user;
        }
        if (!$user_info_columns) {
            throw new AdminException('所填空投人员没有一个是满足空投条件的！');
        }
        foreach ($user_info_columns as $k => $v) {
            // 多个空投数量 $num
            $res = $this->issueOneBoxManyNew((int)$box_id, (int)$num, (int)$v['user_id'], $v['mobile'], 0);
            if (!$res) {
                throw new AdminException('网络错误请稍后重试~~！');
            }
        }

        return true;
    }

    /**
     * @param $mobile
     * @return bool
     */
    public function cmf_check_mobile($mobile)
    {
        if (preg_match('/(^(13\d|14\d|15\d|16\d|17\d|18\d|19\d)\d{8})$/', $mobile)) {
            return true;
        } else {
            if (preg_match('/^\d{1,4}-\d{5,11}$/', $mobile)) {
                if (preg_match('/^\d{1,4}-0+/', $mobile)) {
                    //不能以0开头
                    return false;
                }

                return true;
            }

            return false;
        }
    }

}
